package com.xl.rentkeeper.db.orm;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;

import com.xl.rentkeeper.db.orm.dsl.Table;
import com.xl.rentkeeper.db.orm.util.NamingHelper;
import com.xl.rentkeeper.db.orm.util.ReflectionUtil;
import com.xl.rentkeeper.log.QLog;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Created by zhangjimin on 15/5/1.
 */
public class DBInterface {

    public DBInterface(DBOpenHelper helper) {
        db = helper;
    }
    DBOpenHelper db;
    String getTable(String tableName,Class type) {
        String table = TextUtils.isEmpty(tableName) ? NamingHelper.toSQLName(type):tableName;
        return table;
    }

    public <T> void deleteAll(String tableName,Class<T> type) {
        SQLiteDatabase sqLiteDatabase = db.getDB();
        sqLiteDatabase.delete(getTable(tableName,type), null, null);
    }

    public <T> void deleteAll(String tableName,Class<T> type, String whereClause, String... whereArgs) {
        SQLiteDatabase sqLiteDatabase = db.getDB();
        sqLiteDatabase.delete(getTable(tableName, type), whereClause, whereArgs);
    }


    public <T> void deleteAll(String tableName,Collection<T> objects) {
        SQLiteDatabase sqLiteDatabase = db.getDB();
        try {
            sqLiteDatabase.beginTransaction();
            sqLiteDatabase.setLockingEnabled(false);
            for (T object: objects) {
                delete(db.getDB(), tableName, object);
            }
            sqLiteDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            QLog.d(this, "Error in saving in transaction " + e.getMessage());
        } finally {
            sqLiteDatabase.endTransaction();
            sqLiteDatabase.setLockingEnabled(true);
        }
    }

    void delete(SQLiteDatabase db, String tableName,Object object) {
        deleteAll(tableName,object.getClass(),"dbid=?",String.valueOf( ((DBObjBase)object).dbid) );
    }


    @SuppressWarnings("deprecation")
    public <T> void saveInTx(String tableName,Collection<T> objects) {
        SQLiteDatabase sqLiteDatabase = db.getDB();
        try {
            sqLiteDatabase.beginTransaction();
            sqLiteDatabase.setLockingEnabled(false);
            for (T object: objects) {
                save(db.getDB(), tableName, object);
            }
            sqLiteDatabase.setTransactionSuccessful();
        } catch (Exception e) {
            QLog.d(this, "Error in saving in transaction " + e.getMessage());
        } finally {
            sqLiteDatabase.endTransaction();
            sqLiteDatabase.setLockingEnabled(true);
        }
    }


    public <T> List<T> listAll(String tableName,Class<T> type) {
        return find(tableName,type, null, null, null, null, null);
    }

    public <T> T findById(String tableName,Class<T> type, Long id) {
        List<T> list = find(tableName,type, ORMap.TableKeyId+"=?", new String[]{String.valueOf(id)}, null, null, "1");
        if (list.isEmpty()) return null;
        return list.get(0);
    }


    public void executeQuery(String query, String... arguments) {
        db.getDB().execSQL(query, arguments);
    }

    public <T> List<T> find(String tableName,Class<T> type, String whereClause, String[] whereArgs) {
        return find(tableName,type,whereClause,whereArgs,null,null,null);
    }

    public <T> List<T> find(String tableName,Class<T> type, String whereClause, String[] whereArgs, String groupBy, String orderBy, String limit) {
        SQLiteDatabase sqLiteDatabase = db.getDB();
        T entity;
        List<T> toRet = new ArrayList<T>();
        Cursor c = sqLiteDatabase.query(getTable(tableName,type), null, whereClause, whereArgs,
                groupBy, null, orderBy, limit);
        try {
            while (c.moveToNext()) {
                entity = type.getDeclaredConstructor().newInstance();
                inflate(c, entity);
                toRet.add(entity);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            c.close();
        }
        return toRet;
    }

    public <T> List<T> findWithQuery(Class<T> type, String query, String... arguments) {
        SQLiteDatabase sqLiteDatabase = db.getDB();
        T entity;
        List<T> toRet = new ArrayList<T>();
        Cursor c = sqLiteDatabase.rawQuery(query, arguments);

        try {
            while (c.moveToNext()) {
                entity = type.getDeclaredConstructor().newInstance();
                inflate(c, entity);
                toRet.add(entity);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            c.close();
        }

        return toRet;
    }

    public long save(Object object) {
        return save(db.getDB(), "",object);
    }

    long save(SQLiteDatabase db, String tableName,Object object) {
        if (object instanceof DBObj) {
            ((DBObj)object).preSave();
        }
        List<Field> columns = ReflectionUtil.getTableFields(object.getClass());
        ContentValues values = new ContentValues(columns.size());
        for (Field column : columns) {
            ReflectionUtil.addFieldValueToColumn(values, column, object);
        }
        long id = db.insertWithOnConflict(getTable(tableName,object.getClass()), null, values,
                SQLiteDatabase.CONFLICT_REPLACE);
        ReflectionUtil.setFieldValueForId(object, id);
        QLog.d(this, object.getClass().getSimpleName() + " saved : " + id);
        return id;
    }

    public void inflate(Cursor cursor, Object object) {
        List<Field> columns = ReflectionUtil.getTableFields(object.getClass());

        for (Field field : columns) {
            if (field.getClass().isAnnotationPresent(Table.class)) {
                try {
                    long id = cursor.getLong(cursor.getColumnIndex(NamingHelper.toSQLName(field)));
                    field.set(object, (id > 0) ? findById("",field.getType(), id) : null);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            } else {
                ReflectionUtil.setFieldValueFromCursor(cursor, field, object);
            }
        }
        if (object instanceof DBObj) {
            ((DBObj)object).afterLoad();
        }
    }
}
